안드로이드 5기 2017년 강의 정리 5 (오준석의 생존코딩)
https://www.youtube.com/watch?v=bC_YZkSBTl8&list=PLxTmPHxRH3VWSF7kMcsIaTglWUJZpWeQ9&index=105
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki
MyFirstAndroidApp5ki-master.zip
35일차 36일차 뮤직플레이어
37일차 Parcelable
37일차 Custom Toast, 40일차 다이얼로그 액티비티
38일차 오버워치 앱 리뷰
40일차 다이얼로그 액티비티
41일차 커스텀 뷰
42일차 DataBinding 라이브러리
42일차 45일차 XML파싱 뉴스앱
35일차 36일차 뮤직플레이어
# MediaPlayer overview (android developer)
https://developer.android.com/guide/topics/media/mediaplayer#java
# MusicPlayerActivity.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/activities/MusicPlayerActivity.java
# activity_music_player.xml
# MusicService.java
# PlayerFragment.java
# music_player.xml
# SongFragment.java
# fragment_song.xml
# MusicControllerFragment.java
# music_controller.xml
# ListViewFragment.java
# fragment_list_view.xml
# fragment_song.xml
37일차 Parcelable
# Parcelable (android developer)
https://developer.android.com/reference/android/os/Parcelable
# Parcelables and Bundles (android developer)
https://developer.android.com/guide/components/activities/parcelables-and-bundles#java
# Android Parcelable code generator 플러그인 설치
- File – Settings – Plugins – Android Parcelable code generator 설치
# A typical implementation of Parcelable is:
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
37일차 Custom Toast, 40일차 다이얼로그 액티비티
# CustomDesignActivity.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/activities/CustomDesignActivity.java
# MyUtils.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/utils/MyUtils.java
# activity_custom_design.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/activity_custom_design.xml
# background_toast.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/drawable/background_toast.xml
# activity_dialog_theme.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/activity_dialog_theme.xml
# CircleImageView
https://github.com/hdodenhof/CircleImageView
public class CustomDesignActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_design);
}
public void showToast(View view) {
MyUtils.makeText(this, "나만의 토스트 ㅋㅋㅋ", Toast.LENGTH_SHORT).show();
}
public void showDialogActivity(View view) {
Intent intent = new Intent(this, DialogThemeActivity.class);
intent.putExtra("data", "ㅋㅋㅋㅋㅋㅋ");
intent.putExtra("image", "ㅋㅋㅋㅋㅋㅋ");
startActivity(intent);
}
public void showAlertDialog(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(this,
R.style.Theme_AppCompat_Dialog);
View layout = getLayoutInflater().inflate(R.layout.activity_dialog_theme, null, false);
((TextView) layout.findViewById(R.id.text_view)).setText("ㅋㅋㅋㅋㅋㅋ");
((ImageView) layout.findViewById(R.id.image_view)).setImageResource(R.drawable.man);
builder.setView(layout);
builder.show();
}
}
public class MyUtils {
public static int sum(int a, int b) {
return a + b;
}
public static Toast makeText(Context context, CharSequence message, int duration) {
Toast result = new Toast(context);
View v = LayoutInflater.from(context).inflate(R.layout.custom_toast, null);
TextView tv = (TextView) v.findViewById(R.id.message_text);
tv.setText(message);
result.setView(v);
result.setDuration(duration);
result.setGravity(Gravity.CENTER_HORIZONTAL,
0, -300);
return result;
}
public static String getRealPath(Context context, Uri uri) {
String strDocId = DocumentsContract.getDocumentId(uri);
String[] strSplittedDocId = strDocId.split(":");
String strId = strSplittedDocId[strSplittedDocId.length - 1];
Cursor crsCursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI ,
new String[] {MediaStore.MediaColumns.DATA} ,
"_id=?",
new String []{strId},
null
);
crsCursor.moveToFirst();
String filePath = crsCursor.getString(0);
return filePath;
}
}
# drawable/background_toast.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="4dp"
android:color="#e040fb" />
<corners android:radius="16dp" />
<solid android:color="#e1bee7" />
</shape>
# 다이얼로그 테마 적용 (AndroidManifest.xml)
<activity android:name=".activities.DialogThemeActivity" android:theme="@style/Theme.AppCompat.Dialog" />
# android_dialog_theme.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="aaaa"
android:textSize="24sp"
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
38일차 오버워치 앱 리뷰
# Parceler
https://github.com/johncarl81/parceler
# Shrink your resources
https://developer.android.com/studio/build/shrink-code#shrink-resources
android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
# How to create a release signed apk file using Gradle?
https://stackoverflow.com/questions/18328730/how-to-create-a-release-signed-apk-file-using-gradle
# cmd 로 apk 파일 만들기
gradlew installRelease
41일차 커스텀 뷰
# JoystickView.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/views/JoystickView.java
public class JoystickView extends View {
private Paint mBackgroundPaint = new Paint();
private Paint mJoystickPaint = new Paint();
private float mX = 200; // 0 ~ 400
private float mY = 200; // 0 ~ 400
// 코드로 생성할 때
public JoystickView(Context context) {
this(context, null);
}
// XML에 삽입할 때
public JoystickView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mBackgroundPaint.setColor(Color.BLACK);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mJoystickPaint.setColor(Color.RED);
}
// View의 모양을 그리는 곳
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(200, 200, 200, mBackgroundPaint);
canvas.drawCircle(mX, mY, 100, mJoystickPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
final int historySize = event.getHistorySize();
final int pointerCount = event.getPointerCount();
for (int h = 0; h < historySize; h++) {
for (int p = 0; p < pointerCount; p++) {
mX = event.getHistoricalX(p, h);
mY = event.getHistoricalY(p, h);
if (Math.sqrt(Math.pow(200 - mX, 2) + Math.pow(200 - mY, 2)) > 100) {
return true;
}
// onDraw() 를 호출
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
mX = 200;
mY = 200;
invalidate();
break;
}
return true;
}
// 크기 결정
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// 위치
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
}
42일차 DataBinding 라이브러리
# DataBindingActivity.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/activities/DataBindingActivity.java
# activity_data_binding.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/activity_data_binding.xml
public class DataBindingActivity extends AppCompatActivity implements View.OnClickListener {
private ActivityDataBindingBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this,
R.layout.activity_data_binding);
mBinding.button10.setOnClickListener(this);
}
@Override
public void onClick(View v) {
}
public static class MyFragment extends Fragment {
private FragmentDataBindingBinding mBinding;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_data_binding,
container,
false);
return mBinding.getRoot();
}
}
}
# activity_data_binding.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.myapplication.activities.DataBindingActivity">
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/name_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView9"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView11"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView12"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView13"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<Button
android:id="@+id/button10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button" />
<Spinner
android:id="@+id/spinner2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<SeekBar
android:id="@+id/seekBar2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/checkBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CheckBox" />
</LinearLayout>
</layout>
42일차 XML파싱 뉴스앱
NewsXMLActivity.java
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/java/com/example/myapplication/activities/NewsXMLActivity.java
# activity_news_xml.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/activity_news_xml.xml
# item_news.xml
https://github.com/suwonsmartapp/MyFirstAndroidApp5ki/blob/master/app/src/main/res/layout/item_news.xml
# Parse XML data (android developer)
https://developer.android.com/training/basics/network-ops/xml#java
# Android Studio Java XmlPullParser how to get only certain tag values (stackoverflow.com)
https://stackoverflow.com/questions/48290357/android-studio-java-xmlpullparser-how-to-get-only-certain-tag-values
import android.app.ListActivity;
import android.os.Bundle;
import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import androidx.databinding.DataBindingUtil;
import com.example.myfirstappapplication.databinding.ItemNewsBinding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class NewsXMLActivity extends ListActivity {
private OkHttpClient mClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mClient = new OkHttpClient();
new Thread(new Runnable() {
@Override
public void run() {
try {
Request request = new Request.Builder()
.url("https://news.google.co.kr/news?cf=all&hl=ko&pz=1&ned=kr&output=rss")
.build();
Response response = null;
response = mClient.newCall(request).execute();
final String xml = response.body().string();
// 파싱
final List<News> data = parse(xml);
runOnUiThread(new Runnable() {
@Override
public void run() {
// 어댑터 꽂아주기
NewsAdapter adapter = new NewsAdapter(data);
setListAdapter(adapter);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private List<News> parse(String xml) {
try {
return new NewsParser().parse(xml);
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static class News {
String title;
String link;
String pubDate;
String source;
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("News{");
sb.append("title='").append(title).append('\'');
sb.append(", link='").append(link).append('\'');
sb.append(", pubDate='").append(pubDate).append('\'');
sb.append(", category='").append(source).append('\'');
sb.append('}');
return sb.toString();
}
}
private static class NewsAdapter extends BaseAdapter {
private List<News> mData;
private ItemNewsBinding mmBinding;
public NewsAdapter(List<News> data) {
mData = data;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
mmBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.item_news, parent, false);
convertView = mmBinding.getRoot();
holder = new ViewHolder();
holder.titleTextView = mmBinding.titleText;
holder.dateTextView = mmBinding.dateText;
holder.sourceTextView = mmBinding.sourceText;
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
News news = (News) getItem(position);
holder.titleTextView.setText(news.title);
holder.dateTextView.setText(news.pubDate);
holder.sourceTextView.setText(news.source);
return convertView;
}
}
private static class ViewHolder {
TextView titleTextView;
TextView dateTextView;
TextView sourceTextView;
}
private static class NewsParser {
public List<News> parse(String xml) throws XmlPullParserException, IOException {
List<News> newsList = new ArrayList<>();
News news = null;
String text = "";
boolean isItem = false;
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(new StringReader(xml));
int eventType = parser.getEventType();
while(eventType != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
if (tagName.equals("item")) {
news = new News();
isItem = true;
}
break;
case XmlPullParser.TEXT:
if (isItem) {
text = parser.getText();
}
break;
case XmlPullParser.END_TAG:
if (isItem) {
if (tagName.equals("item")) {
newsList.add(news);
isItem = false;
} else if (tagName.equals("title")) {
news.title = text;
} else if (tagName.equals("link")) {
news.link = text;
} else if (tagName.equals("source")) {
news.source = text;
} else if (tagName.equals("pubDate")) {
news.pubDate = text;
}
}
break;
default:
}
eventType = parser.next();
}
return newsList;
}
}
}
# item_news_xml.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
# item_news.xml
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@+id/date_text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/date_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:text="TextView"
app:layout_constraintEnd_toStartOf="@+id/source_text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_text" />
<TextView
android:id="@+id/source_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/date_text"
app:layout_constraintTop_toBottomOf="@+id/title_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>